package com.dylan.channel.v10.handler;

import com.dylan.channel.v10.future.ChannelPromise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.net.SocketAddress;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;

/***
 * @Author dylan
 * @Description: ChannelHandler关注的事件类
 * @Create: 2024/9/25 14:48
 */
final class ChannelHandlerMask {
    final static Logger logger = LoggerFactory.getLogger(ChannelHandlerMask.class);


    static final int MASK_EXCEPTION_CAUGHT = 1;
    static final int MASK_BIND = 1 << 9;
    static final int MASK_CONNECT = 1 << 10;
    static final int MASK_DISCONNECT = 1 << 11;
    static final int MASK_CLOSE = 1 << 12;
    static final int MASK_DEREGISTER = 1 << 13;
    static final int MASK_READ = 1 << 14;
    static final int MASK_WRITE = 1 << 15;
    static final int MASK_FLUSH = 1 << 16;

    static final int MASK_ONLY_OUTBOUND = MASK_BIND | MASK_CONNECT | MASK_DISCONNECT |
            MASK_CLOSE | MASK_DEREGISTER | MASK_READ | MASK_WRITE | MASK_FLUSH;
    private static final int MASK_ALL_OUTBOUND = MASK_EXCEPTION_CAUGHT | MASK_ONLY_OUTBOUND;

    public static int mask(Class<? extends ChannelHandler> clazz) {
        Integer mask = null;
        try {
            //在这里，通过mask0方法，计算出该消息处理器感兴趣的事件
            mask = mask0(clazz);
        } catch (PrivilegedActionException e) {
            throw new RuntimeException(e);
        }
        return mask;
    }

    /**
     * @Author: PP-jessica
     * @Description:计算ChannelHandler感兴趣的事件类型
     */
    private static int mask0(Class<? extends ChannelHandler> handlerType) throws PrivilegedActionException {
        //首先先定义一个要返回的感兴趣的事件掩码
        int mask = 0;

        //先判断该处理器是否为出站处理器，其实这里应该也把判断入站处理器的逻辑补充完整的
        //但那样代码就太多了，而且手写的netty中代码很全，和源码一致，所以，这里就只保留
        //三个出站处理器的方法
        if (ChannelOutboundHandler.class.isAssignableFrom(handlerType)) {
            //如果是出站处理器，就得到出站处理器感兴趣的总的事件合集
            mask = mask | MASK_ALL_OUTBOUND;
            //判断出站处理器是否对bind事件感兴趣，通过isSkippable方法判断该处理器的bind方法上
            //是否有@Skip注解，如果有这个注解，就返回true，说明该处理器对该事件不感兴趣，做与运算
            //把该事件的常量值从事件合集中减去即可
            if (isSkippable(handlerType, "bind", AbstractChannelHandlerContext.class, SocketAddress.class, ChannelPromise.class)) {
                mask &= ~MASK_BIND;
            }
            if (isSkippable(handlerType, "write", AbstractChannelHandlerContext.class, Object.class, ChannelPromise.class)) {
                mask &= ~MASK_WRITE;
            }
            if (isSkippable(handlerType, "flush", AbstractChannelHandlerContext.class)) {
                mask &= ~MASK_FLUSH;
            }
        }
        return mask;
    }


    //该方法就是用来判断出站处理器中的相应方法上有没有@Skip注解
    private static boolean isSkippable(final Class<?> handlerType, final String methodName, final Class<?>... paramTypes) throws PrivilegedActionException {
        //下面是Java反射中的原生方法，可去Java源码中查看学习
        return AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
            @Override
            public Boolean run() throws Exception {
                Method m;
                try {
                    //在这里得到出站处理器中是否存在对应名称的方法
                    m = handlerType.getMethod(methodName, paramTypes);
                } catch (NoSuchMethodException e) {
                    logger.debug("Class {} missing method {}, assume we can not skip execution", handlerType, methodName, e);
                    return false;
                }
                //如果该方法不为null，并且该方法有@Skip注解，说明该方法对此出站事件
                //不感兴趣，返回true
                return m != null && m.isAnnotationPresent(Skip.class);
            }
        });
    }
}
